/*
  Losu Copyright Notice
  --------------------
    Losu is an open source programming language project under the MIT license
  that can be used for both academic and commercial purposes. There are no
  fees, no royalties, and no GNU-like "copyright" restrictions. Losu qualifies
  as open source software. However, Losu is not public property, and founder
  'chen-chaochen' retains its copyright.

    Losu has been registered with the National Copyright Administration of the
  People's Republic of China, and adopts the MIT license as the copyright
  licensing contract under which the right holder conditionally licenses its
  reproduction, distribution, and modification rights to an unspecified public.

    If you use Losu, please follow the public MIT agreement or choose to enter
  into a dedicated license agreement with us.

  The MIT LICENSE is as follows
  --------------------
  Copyright  2020 - now  chen-chaochen

    Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the “Software”), to
  deal in the Software without restriction, including without limitation the
  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/

#include "stdlib.lsh.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef __config_losucore_encode_nativeUTF8

#ifdef __config_losucore_encode_winGBK
#error "Invalid encode config"
#endif

#define toSys(vm, s) (s)
#define toLosu(vm, s) (s)

#endif

#ifdef __config_losucore_encode_winGBK

#ifdef __config_losucore_encode_nativeUTF8
#error "Invalid encode config"
#endif

#define toSys(vm, s) (charset_newObj_toSys (vm, s))
#define toLosu(vm, s) (charset_newObj_toLosu (vm, s))

#endif

#if 10 <= __config_losucore_platform_template                                 \
    && __config_losucore_platform_template < 20 /* linux-all */
#include <unistd.h>
#define __config_losupkg_stdlib_platform_sleep(ms) (usleep (ms * 1000))
#endif

#if 20 <= __config_losucore_platform_template                                 \
    && __config_losucore_platform_template < 30 /* windows-all */
#include <windows.h>
#define __config_losupkg_stdlib_platform_sleep(ms) (Sleep (ms))
#endif

#define __config_losupkg_stdlib_buffszie 1024

/**
 * @fn print( ...:str )
 * @brief 打印一行信息, 行末换行
 */
int32_t
__losuh__func_print_io_stdlib (LosuVm *vm)
{
  for (int32_t i = 0; i < arg_num (vm); i++)
    printf ("%s", toSys (vm, arg_getstr (vm, i + 1)));
  printf ("\n");
  return 0;
}
/**
 * @fn input( text:str = '' ,isline:bool = null )
 * @brief 输入数据, 如果 isline 非空, 输入一行
 * @param text 输入提示
 * @return str 输入的数据
 */
int32_t
__losuh__func_input_io_stdlib (LosuVm *vm)
{
  char text[__config_losupkg_stdlib_buffszie] = { 0 };
  _l_bool isline = (arg_gettype (vm, 2) != LosuTypeDefine_null);
  printf ("%s", toSys (vm, arg_getstr (vm, 1)));

  if (isline)
    {
      if (fgets (text, __config_losupkg_stdlib_buffszie, stdin) == NULL)
        vm_error (vm, LosuErrorCode_Runtime, "Input overflow");
    }
  else
    {
      if (scanf ("%s", text) != 1)
        vm_error (vm, LosuErrorCode_Runtime, "Input failed");
    }
  /*  fmt */
  for (int32_t i = 0; text[i]; i++)
    if (text[i] == '\n' || text[i] == '\r')
      {
        text[i] = 0;
        break;
      }
  arg_returnstr (vm, toLosu (vm, text));
  return 1;
}
/**
 * @fn sleep( ms:int )
 * @brief 睡眠 ms 毫秒
 */
int32_t
__losuh__func_sleep_io_stdlib (LosuVm *vm)
{
  __config_losupkg_stdlib_platform_sleep (((int32_t)arg_getnum (vm, 1)));
  return 0;
}
/**
 * @fn exit( code:int = 0 )
 * @brief 退出程序 code 为退出码
 */
int32_t
__losuh__func_exit_io_stdlib (LosuVm *vm)
{
  exit ((int32_t)arg_getnum (vm, 1));
  return 0;
}
/**
 * @fn system( cmd:str )
 * @brief 执行系统命令，返回值
 */
int32_t
__losuh__func_system_io_stdlib (LosuVm *vm)
{
  int32_t i = system (toSys (vm, arg_getstr (vm, 1)));
  arg_returnnum (vm, i);
  return 1;
}
/**
 * @fn type( obj:any )
 * @brief 返回 obj 的类型
 * @return type obj的类型
 */
int32_t
__losuh__func_type_type_stdlib (LosuVm *vm)
{
  arg_returnstr (vm, arg_gettypeStr (vm, 1));
  return 1;
}
/**
 * @fn val( obj:any )
 * @brief 将 obj 数值化, 进行 ::->num 转换
 * @return val:float obj的值
 */
int32_t
__losuh__func_val_type_stdlib (LosuVm *vm)
{
  arg_returnnum (vm, arg_getnum (vm, 1));
  return 1;
}
/**
 * @fn int( obj:any )
 * @brief 将 obj 转换为整数, 先进行 ::->num 转换, 然后进行 num->int 转换
 * @return val:int obj的值
 */
int32_t
__losuh__func_int_type_stdlib (LosuVm *vm)
{
  arg_returnnum (vm, (int32_t)arg_getnum (vm, 1));
  return 1;
}
/**
 * @fn str( obj:any )
 * @brief 将 obj 转换为字符串, 进行 ::->str 转换
 * @return val:str obj的值
 */
int32_t
__losuh__func_str_type_stdlib (LosuVm *vm)
{
  arg_returnstr (vm, arg_getstr (vm, 1));
  return 1;
}
/**
 * @fn char( asc:int )
 * @brief 将 asc 转换为字符
 * @return val:str asc 对应的字符
 */
int32_t
__losuh__func_char_type_stdlib (LosuVm *vm)
{
  char c[2] = { 0 };
  c[0] = (char)arg_getnum (vm, 1);
  arg_returnstr (vm, c);
  return 1;
}
/**
 * @fn asc( char:str )
 * @brief 将 char 转换为 ascii 码
 * @return val:int char 对应的 ascii 码
 */
int32_t
__losuh__func_asc_type_stdlib (LosuVm *vm)
{
  uint8_t asc = arg_getstr (vm, 1)[0];
  arg_returnnum (vm, (_l_number)((int32_t)(asc)));
  return 1;
}
/**
 * @fn vm.version( fmt:bool = null )
 * @brief 返回当前 vm 的版本号
 * @return version:str/int vm的版本号, 如果 fmt 为 null, 返回字符串,
 * 否则返回整数
 */
/**
 * @fn len( s:str )
 * @brief 返回字符串 s 的长度
 * @return val:int 字符串 s 的长度
 */
int32_t
__losuh__func_len_type_stdlib (LosuVm *vm)
{
  arg_returnnum (vm, obj_getstrlen (vm, arg_get (vm, 1)));
  return 1;
}
/**
 * @fn vm.version( fmt:bool = null )
 * @brief 返回当前 vm 的版本号
 * @return version:str/int vm的版本号, 如果 fmt 为 null, 返回字符串,
 * 否则返回整数
 */
int32_t
__losuh__func_version_vm_vm_stdlib (LosuVm *vm)
{
  _l_bool n = arg_gettype (vm, 2) != LosuTypeDefine_null;
  if (n)
    arg_returnnum (vm, LosuVersionNum);
  else
    arg_returnstr (vm, LosuVersion);
  return 1;
}
/**
 * @fn vm.build()
 * @brief 返回当前 vm 的构建信息
 * @return build:str vm的构建信息
 */
int32_t
__losuh__func_build_vm_vm_stdlib (LosuVm *vm)
{
  arg_returnstr (vm, LosuBuild);
  return 1;
}
int32_t
__losuh__func_arch_vm_vm_stdlib (LosuVm *vm)
{
  arg_returnstr (vm, LosuArch);
  return 1;
}

/**
 * @fn vm.copyright()
 * @brief 返回当前 vm 的版权信息
 * @return copyright:str vm的版权信息
 */
int32_t
__losuh__func_copyright_vm_vm_stdlib (LosuVm *vm)
{
  arg_returnstr (vm, LosuCopyright);
  return 1;
}
/**
 * @fn eval( spt:str ,name:str = 'unknown' )
 * @brief 解释执行 spt 字符串
 * @return ... 一到多个脚本返回值
 */
int32_t
__losuh__func_eval_vm_stdlib (LosuVm *vm)
{
  const char *s = arg_getstr (vm, 1);
  const char *name = arg_getstr (vm, 2);
  LosuObj *oldtop = vm->top;
  if (name[0] == '\0')
    name = "unknown";
  if (!s) /* s == NULL */
    return 0;
  int32_t sta = vm_loadstring (vm, s, name);
  if (sta) /* error */
    return 0;
  stack_call (vm, 0, -1);
  return vm->top - oldtop;
}
/**
 * @fn exec( fn:str )
 * @breif 执行 fn 文件 （源代码或预编译的二进制）
 * @return ... 一到多个脚本返回值
 */
int32_t
__losuh__func_exec_vm_stdlib (LosuVm *vm)
{
  const char *fn = arg_getstr (vm, 1);
  LosuObj *oldtop = vm->top;
  if (fn[0] == '\0')
    return 0;
  int32_t sta = vm_loadfile (vm, toSys (vm, fn));
  if (sta) /* error */
    return 0;
  stack_call (vm, 0, -1);
  return vm->top - oldtop;
}
/**
 * @fn error( msg:str = '' )
 * @brief 抛出异常 msg 为运行时异常信息
 */
int32_t
__losuh__func_error_vm_stdlib (LosuVm *vm)
{
  vm_error (vm, LosuErrorCode_Runtime, arg_getstr (vm, 1));
  return 0;
}
/**
 * @fn warning( msg:str = '' )
 * @brief 抛出警告 msg 为运行时警告信息
 */
int32_t
__losuh__func_warning_vm_stdlib (LosuVm *vm)
{
  vm_warning (vm, arg_getstr (vm, 1));
  return 0;
}
/**
 * @fn mem.nowuse( self, p:str = 'B' )
 * @brief 返回当前内存使用量
 * @return size:int 当前内存使用量，单位为 p(B,KB,M)
 */
int32_t
__losuh__func_nowuse_mem_mem_stdlib (LosuVm *vm)
{
  uint8_t p = arg_getstr (vm, 2)[0];
  int32_t e = 1;
  switch (p)
    {
    case 'K':
      e = 1024;
      break;
    case 'M':
      e = 1024 * 1024;
    default:
      break;
    }
  arg_returnnum (vm, (_l_number)((_l_number)gc_getmemNow (vm) / (_l_number)e));
  return 1;
}
/**
 * @fn mem.maxuse( self, p:str = 'B' )
 * @brief 返回最大内存使用量
 * @return size:int 最大内存使用量，单位为 p(B,KB,M)
 */
int32_t
__losuh__func_maxuse_mem_mem_stdlib (LosuVm *vm)
{
  uint8_t p = arg_getstr (vm, 2)[0];
  int32_t e = 1;
  switch (p)
    {
    case 'K':
      e = 1024;
      break;
    case 'M':
      e = 1024 * 1024;
    default:
      break;
    }
  arg_returnnum (vm, (_l_number)((_l_number)gc_getmemMax (vm) / (_l_number)e));
  return 1;
}
/**
 * @fn mem.setgc( self, size:int ,p:str = 'B' )
 * @brief 设置垃圾回收阈值, 单位为 p(B,KB,M)
 */
int32_t
__losuh__func_setgc_mem_mem_stdlib (LosuVm *vm)
{
  uint8_t p = arg_getstr (vm, 3)[0];
  int32_t e = 1;
  switch (p)
    {
    case 'K':
      e = 1024;
      break;
    case 'M':
      e = 1024 * 1024;
    default:
      break;
    }

  gc_setmax (vm, (_l_gcint)(arg_getnum (vm, 2) * e));
  return 0;
}
/**
 * @fn mem.gc()
 * @brief 手动触发垃圾回收
 * @return b:int 0-未进行垃圾回收, 1-进行了垃圾回收
 */
int32_t
__losuh__func_gc_mem_mem_stdlib (LosuVm *vm)
{
  arg_returnnum (vm, gc_collect (vm));
  return 1;
}